
在大型語言模型(LLM)落地私有地端(On-Premise)的架構中,推論服務冷啟動(Cold Start)與模型權重載入速度是維運的核心指標之一。
在先前的架構規劃中,曾推估在 10GbE 網路環境下載入約 63.4 GB 的開源大模型冷啟動需要約 60 秒,並在掛載參數中設定了 nconnect=4。然而,這個 4 到底是不是最佳配置?模型權重究竟該走 NFS 集中管理,還是切 iSCSI 甚至放本機?
今天來把測試工具與標準方法確定好,將先前的理論推估替換為實測資料,同時探討 NAS 儲存池的 RAID 拓撲選擇,因為在實際架構中,載入時間的物理上限往往是由 NAS 的磁碟陣列吞吐量所決定。
這邊先揭露實測結論:
nconnect(從 1 到 8)完全沒有帶來效能提升。mmap 類型的載入器速度慢了將近一倍。為了釐清不同儲存路徑對推論服務的影響,這次針對以下三條 AI 算力節點需要用到的儲存路徑進行對比:
| 儲存路徑 | 在架構中的位置 | 適用場景 | 本次量測目標 |
|---|---|---|---|
| 本機 NVMe SSD | GPU 運算節點本機系統碟 | 本機只保留快取、排程暫存與 KV 溢出 | 量測節點的硬體極限,作為基準對照組 |
| NFS 網路共享 | NAS 匯出的集中模型目錄 /srv/models |
全叢集共享模型權重,支援彈性擴展 | 集中模型庫的真實冷啟動時間 |
| iSCSI 區塊儲存 | NAS 上的 LUN,掛載為節點本機區塊裝置 | 單一專屬節點的高速讀取需求 | 驗證區塊協定是否比檔案協定(NFS)更快,評估適用性 |
說明:iSCSI 的定位
iSCSI LUN 屬於單一 Initiator 的區塊設備,多個節點無法在沒有叢集檔案系統的前提下同時掛載讀寫。因此它無法替代 NFS 作為集中模型庫,但可以作為本機磁碟容量不足時的延伸選項。
此數值明顯低於 10GbE 頻寬上限,意味著 NFS 的瓶頸在於 NAS 磁碟組而非網路本身。
測試使用三種不同儲存池結構的 NAS 環境進行對照:
| 測試機 | 磁碟與儲存池配置 | 記憶體 | 測試角色 |
|---|---|---|---|
| 四碟測試 | 4 顆 10TB 7200 RPM,RAIDZ1(ZFS) | 16 GB | 標準硬碟環境,主要測試對象 |
| 單碟測試 | 1 顆 2.5 吋 HDD + 256GB SSD 讀取快取 | 16 GB | 驗證讀取快取與極限單碟情境 |
| 模型庫正式 | 6 顆 4TB 5400 RPM,RAIDZ2 + 鏡射池 | 64 GB | 參考用儲存設備 |
gpt-oss-120b safetensors 格式(15 個檔案,共計 65.28 GB / 60.79 GiB)。在進行效能基準測試時,若忽視底層快取機制,量測資料往往毫無參考價值。
在 Linux 客戶端執行 echo 3 > /proc/sys/vm/drop_caches 只能清空節點本機的 Page Cache,無法清空 NAS 端的 ZFS ARC(Adaptive Replacement Cache)。因此:
zpool iostat 同步監看 NAS 端硬碟是否有實際 I/O 發生。此外,儲存系統的透明快取層更具隱蔽性。例如單碟測試機量測 60 GiB 資料時,連續跑出 0.47 GiB/s 的異常高速,深入檢查 /proc/diskstats 後才發現,資料全部被讀取快取專用的 SSD 攔截處理,磁碟本體幾乎無 I/O,所以測試前,務必確認儲存池是否掛載了 Cache 設備。
nconnect 的特性:同一個 NAS 伺服器在客戶端僅有「第一個掛載點」所指定的 nconnect 會生效。後續掛載若未重新建立連線,設定值將被忽略。量測時需以 ss -tn 核對實際建立的 TCP 連線數。readahead 的決定性影響:
/sys/block/<dev>/queue/read_ahead_kb。/sys/class/bdi/<Major:Minor>/read_ahead_kb 控制,核心預設僅 128 KB。mmap 機制運作的載入器,128 KB 耗時高達 197 秒,調大至 15 MB 則大幅縮短至 114 秒。不同推論框架對權重檔的載入機制完全不同:
mmap)逐頁存取。這兩種模式對儲存架構帶來的壓力差異巨大,測試工具必須針對循序讀取(readinto)與映射存取(mmap)分別量測。
在進行基準測試時,初測 65 GB 模型透過 NFS 耗時高達 500 秒以上,在 NAS 本機直接讀取亦需 512 秒。觀察 I/O 發現每次讀取區塊極度破碎(僅約 64 KB)。將開機超過五天跑很多服務可能造成一些影響的 NAS 重啟後,相同測試透過 NFS 縮短至 158 秒,本機讀取恢復至 105 秒。在進行任何正式測試前,記錄設備的 Uptime 是必要的驗證步驟。
spb.py為了消除上述陷阱,設計了輕量化量測工具 spb.py(使用 Python 標準函式庫),重點在於完整記錄量測當下的環境條件。
# 循序讀取測試(清除本機快取,量測 3 輪)
python3 spb.py run --path "/srv/models/llm/<model-path>" --label nfs-cold --rounds 3 --drop-caches
# 記憶體映射模式測試
python3 spb.py run --path "/srv/models/gguf/<model-path>" --label nfs-mmap --mode mmap
# 產出彙整報告
python3 spb.py report results.jsonl
read_ahead_kb 以及實體網卡交涉速率(避免 10GbE 降速至 2.5GbE/1GbE 卻未被察覺)。posix_fadvise(POSIX_FADV_DONTNEED) 對指定檔案進行精準快取釋放。儲存載入的上限有一半取決於 NAS 磁碟組的配置。磁碟陣列一旦建立,日後變更需要龐大的搬遷成本。
如果以市面上主流的 4 槽機種(4 顆 8TB)與大型 16 槽機架式儲存設備(16 顆 18TB)為例,計算實體容量與可用空間:
| 設備與磁碟配置 | 資料碟數量 | 理論計算容量 | 扣除 ZFS 保留(9%) | 80% 安全容量線 | 容錯上限 |
|---|---|---|---|---|---|
| 4 槽,4×8 TB(RAID 5 / RAIDZ1) | 3 | 21.8 TiB | 19.8 TiB | 15.9 TiB | 任意 1 顆 |
| 4 槽,4×8 TB(RAID 6 / RAIDZ2) | 2 | 14.6 TiB | 13.2 TiB | 10.6 TiB | 任意 2 顆 |
| 4 槽,4×8 TB(RAID 10) | 2 | 14.6 TiB | 13.2 TiB | 10.6 TiB | 每組鏡射各 1 顆 |
| 16 槽,16×18 TB(RAID 60:2×8 碟 RAIDZ2) | 12 | 196.5 TiB | 178.6 TiB | 142.9 TiB | 每組各 2 顆 |
| 16 槽,16×18 TB(單一 16 碟 RAIDZ2) | 14 | 229.2 TiB | 208.3 TiB | 166.7 TiB | 全池任意 2 顆 |
| 16 槽,16×18 TB(RAID-TP:3 碟容錯) | 13 | 212.8 TiB | 193.4 TiB | 154.8 TiB | 全池任意 3 顆 |
容量計算備註
- 「扣除 9%」源自 ZFS 的 Allocation Slop Space、RAIDZ Padding 與系統保留空間,實機與理論公式會有約 9.1% 的固定落差。
- 80% 安全線:ZFS 使用率超過 80% 後效能將明顯下滑,因此規劃可用容量時應以 80% 為上限基準。
4 槽機種:首選 RAIDZ1
若預估模型庫初期需求約 2 至 4 TB,4顆硬碟組 RAIDZ1 或 RAID5 的可用空間相當充裕。若選 RAIDZ2 會損失高達 50% 原始容量,代價過高。重建風險需透過異地冷備份機制來承擔。
16 槽機種:首選 RAID 60(雙 RAIDZ2 群組)
18TB 大容量硬碟重建需耗時超過 30 小時。如果採用「單一 16 碟寬 RAIDZ2」,重建時必須同時讀取其餘 15 顆硬碟,期間若再損壞 2 顆即全池損毀,改採 RAID 60(拆分為兩組 8 碟 RAIDZ2),重建僅需同組的 7 顆硬碟參與 I/O,大幅縮小損壞風險範圍。
以下測試資料量均為 65.28 GB,冷讀取狀態均透過 NAS 端 zpool iostat 確認真實磁碟讀取量,取多次量測之中位數。
| 測試標籤 | 條件與參數 | 耗時(秒) | 說明與瓶頸分析 |
|---|---|---|---|
nvme-cold |
本機 NVMe,readahead 8MB | 11.7 | 基準組,傳輸率約 5.2 GiB/s |
nas-dd |
NAS 本機執行 dd(bs=8M) |
98 | 磁碟陣列物理吞吐上限(約 666 MB/s) |
nfs-cold-1 |
NFS 掛載,nconnect=1 |
106 | 瓶頸卡在 NAS 磁碟,受網路影響小 |
nfs-cold-4 |
NFS 掛載,nconnect=4 |
109 | 效能無顯著差異 |
nfs-cold-8 |
NFS 掛載,nconnect=8 |
110 | 增加 TCP 連線無法突破硬碟上限 |
nfs-warm |
節點 Page Cache 命中 | 2.2 | 記憶體傳輸速度 |
nfs-mmap-cold |
NFS mmap,readahead 128KB | 197 | 預設 readahead 導致大量細碎請求 |
nfs-mmap-cold |
NFS mmap,readahead 15MB | 114 | 調整 readahead 後接近循序讀取水準 |
iscsi-cold |
iSCSI LUN(預設 8K 區塊),ext4 | 167 | 細碎區塊開銷過大,比 NFS 慢 55% |
iscsi-cold |
iSCSI LUN(改為 128K 區塊),ext4 | 88 | 區塊放大後優於 NFS,甚至超越本機檔案系統 |
資料洞察:
nconnect 毫無效果。| 測試標籤 | 系統狀態 | 耗時(秒) | 吞吐換算 |
|---|---|---|---|
nfs-cold |
運行 5 天未重啟,readahead 15MB | 497 至 562 | 約 116 MB/s |
nas-dd |
運行 5 天未重啟,本機讀取 | 512 | 瓶頸位於儲存核心內部 |
nfs-cold |
重啟 NAS 系統後 | 158 | 速度提升 3.5 倍 |
nas-dd |
重啟 NAS 系統後 | 105 | 回復至預期效能 |
在相同運算節點上,分別透過 llama.cpp 與 vLLM(v0.30.0)實際載入 65GB 模型:
| 推論引擎 | 儲存來源 | 載入模式 | 完整就緒時間(秒) |
|---|---|---|---|
| llama.cpp | 本機 NVMe | 循序讀取(--load-mode none) |
16 |
| llama.cpp | 本機 NVMe | 記憶體映射(--load-mode mmap) |
36 |
| llama.cpp | 10GbE NFS | 循序讀取(--load-mode none) |
123 |
| llama.cpp | 10GbE NFS | 記憶體映射(--load-mode mmap) |
219 |
| vLLM | 本機 NVMe | 預設載入 | 503 至 509(純載入權重:435 至 441 秒) |
| vLLM | 10GbE NFS | 預設載入 | 436 至 496(純載入權重:362 至 425 秒) |
推論引擎的關鍵特性:
mmap 需要 219 秒,停用 mmap 改走循序讀取(--load-mode none,舊版參數為 --no-mmap),時間立即降至 123 秒,省下近 100 秒的開銷。# 取得測試腳本
git clone "https://github.com/ivanusto/storage-path-bench" && cd storage-path-bench
MODEL_PATH="/srv/models/llm/<your-model-dir>"
# 執行冷讀取測試
python3 spb.py run --path "$MODEL_PATH" --label nfs-cold-4 --rounds 3 --drop-caches
# 執行熱快取讀取測試
python3 spb.py run --path "$MODEL_PATH" --label nfs-warm --rounds 2
# 產出彙整資料
python3 spb.py report results.jsonl
# 卸載並使用指定參數重新掛載
sudo umount /srv/models
sudo mount -t nfs4 -o ro,vers=4.1,hard,noatime,nconnect=8,rsize=1048576 "<NAS_IP>:/models" /srv/models
# 驗證實際 TCP 連線數是否為 8
ss -tn 'dst <NAS_IP>:2049' | tail -n +2 | wc -l
# 檢視目前掛載點的 readahead(預設通常為 128 KB)
cat /sys/class/bdi/$(mountpoint -d /srv/models)/read_ahead_kb
# 提高 NFS 掛載點 readahead 至 15 MB
echo 15360 | sudo tee /sys/class/bdi/$(mountpoint -d /srv/models)/read_ahead_kb
提示:NFS 掛載點的
read_ahead_kb在每次重新掛載後會重設為預設值,建議建立 systemd service 或 udev 規則在開機時自動套用。
在 NAS 上直接排查磁碟吞吐,避免網路層干擾:
# 在 NAS 本機進行磁碟循序讀取
cd "/share/models/llm/<your-model-dir>"
time sh -c 'for f in model-*.safetensors; do dd if="$f" of=/dev/null bs=8M; done'
# 開啟另一個終端機監控 ZFS 磁碟狀態與讀取量
zpool iostat -v 5
依據本次實測結果,針對 65GB 規模的大語言模型冷啟動規劃,我後來做了調整,之後就多是以這個版本進行實作囉:
--load-mode none,將載入時間從 3.7 分鐘壓回 2 分鐘邊界。